#include "AlgoEvaluatorModule11MandatoryAreaCompletion.h"
#include <vector>

// Helper function to check if a point is inside a polygon using the Ray Casting algorithm.
// TODO: Move this to a common utility file to avoid duplication.
static bool is_inside_polygon(const algobase::Coord& point, const algobase::Polygon& polygon)
{
    if (polygon.empty()) {
        return false;
    }
    int intersections = 0;
    for (size_t i = 0, j = polygon.size() - 1; i < polygon.size(); j = i++) {
        if (((polygon[i][1] > point[1]) != (polygon[j][1] > point[1])) &&
            (point[0] < (polygon[j][0] - polygon[i][0]) * (point[1] - polygon[i][1]) / (polygon[j][1] - polygon[i][1]) + polygon[i][0])) {
            intersections++;
        }
    }
    return intersections % 2 == 1;
}

class algo::AlgoEvaluatorModule11MandatoryAreaCompletionPrivate
{
public:
};

algo::AlgoEvaluatorModule11MandatoryAreaCompletion::AlgoEvaluatorModule11MandatoryAreaCompletion()
	: d(new AlgoEvaluatorModule11MandatoryAreaCompletionPrivate)
{
}

algo::AlgoEvaluatorModule11MandatoryAreaCompletion::~AlgoEvaluatorModule11MandatoryAreaCompletion()
{
	delete d;
}

std::vector<algobase::EvaluateEnvirDataType> algo::AlgoEvaluatorModule11MandatoryAreaCompletion::getNeedType()
{
	return std::vector<algobase::EvaluateEnvirDataType>();
}

int algo::AlgoEvaluatorModule11MandatoryAreaCompletion::exec(const algobase::Path& path, double& result, algobase::EvaluateModuleCoords& indexCoords)
{
	// Ensure the output coords vector has the correct size, even if unused.
	indexCoords.resize(path.size());
	for(size_t i = 0; i < path.size(); ++i) {
		indexCoords[i].Assign(path[i]);
	}

	auto task_config = getTaskConfig();
	const auto& mandatory_areas_it = task_config.mTypePolygons.find(algobase::AT_Mandatory);

	if (mandatory_areas_it == task_config.mTypePolygons.end() || mandatory_areas_it->second.empty()) {
		// No mandatory areas defined, so the completion is 100%.
		result = 1.0;
		return 0;
	}
	const auto& mandatory_polygons = mandatory_areas_it->second;
	size_t total_areas = mandatory_polygons.size();

	if (path.empty()) {
		result = 0.0; // Empty path cannot satisfy any area.
		return 0;
	}

	std::vector<bool> satisfied(total_areas, false);
	int satisfied_count = 0;

	for (const auto& point : path) {
		algobase::Coord current_pos = { point.mCoordZ[0], point.mCoordZ[1] };
		for (size_t i = 0; i < total_areas; ++i) {
			if (!satisfied[i] && is_inside_polygon(current_pos, mandatory_polygons[i])) {
				satisfied[i] = true;
				satisfied_count++;
			}
		}
		// Optimization: if all areas are satisfied, no need to check further.
		if (satisfied_count == total_areas) {
			break;
		}
	}

	// The raw value is the ratio of satisfied areas to the total number of mandatory areas.
	result = calcScore(static_cast<double>(satisfied_count) / static_cast<double>(total_areas));

	return 0;
}

double algo::AlgoEvaluatorModule11MandatoryAreaCompletion::calcScore(double rawValue) const
{
	return rawValue * getWeight();
}

double algo::AlgoEvaluatorModule11MandatoryAreaCompletion::calcRaw(const std::vector<double>& rawValues) const
{
	return 0.0;
}
